package com.gitee.helioz.openai.api;

import com.gitee.helioz.openai.common.util.http.HttpFilePart;
import com.gitee.helioz.openai.common.util.http.HttpProxyConfig;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.hc.client5.http.auth.AuthScope;
import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
import org.apache.hc.client5.http.classic.methods.HttpDelete;
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.classic.methods.HttpPost;
import org.apache.hc.client5.http.config.ConnectionConfig;
import org.apache.hc.client5.http.entity.UrlEncodedFormEntity;
import org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder;
import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider;
import org.apache.hc.client5.http.impl.classic.BasicHttpClientResponseHandler;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.NameValuePair;
import org.apache.hc.core5.http.io.entity.StringEntity;
import org.apache.hc.core5.http.message.BasicNameValuePair;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 〈httpclient5实现类〉
 *
 * @author <a href="https://gitee.com/helioz">Morning Starr</a>
 */
@Slf4j
public class OpenAiHttpClientServiceImpl extends BaseOpenAiServiceImpl {

    private CloseableHttpClient httpClient;
    private HttpClientBuilder httpClientBuilder;

    private final BasicHttpClientResponseHandler responseHandler = new BasicHttpClientResponseHandler();

    public OpenAiHttpClientServiceImpl() {
        super();
    }

    public OpenAiHttpClientServiceImpl(HttpProxyConfig httpProxyConfig) {
        super(httpProxyConfig);
    }

    public OpenAiHttpClientServiceImpl(HttpClientBuilder httpClientBuilder) {
        this.httpClientBuilder = httpClientBuilder;
    }

    public OpenAiHttpClientServiceImpl(HttpClientBuilder httpClientBuilder, HttpProxyConfig httpProxyConfig) {
        super(httpProxyConfig);
        this.httpClientBuilder = httpClientBuilder;
    }

    @Override
    protected void initHttp() {
        if (this.httpClientBuilder == null) {
            this.httpClientBuilder = HttpClientBuilder.create();
            // 创建HttpClient线程池管理器
            PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
            // 设置超时时间
            connectionManager.setDefaultConnectionConfig(ConnectionConfig.custom()
                    .setConnectTimeout(5000, TimeUnit.MILLISECONDS)
                    .setSocketTimeout(5000, TimeUnit.MILLISECONDS)
                    .build());
            // 设置最大连接数
            connectionManager.setMaxTotal(200);
            // 设置每个路由最大连接数
            connectionManager.setDefaultMaxPerRoute(50);
            this.httpClientBuilder.setConnectionManager(connectionManager);
            // 设置代理
            if (this.getHttpProxyConfig() != null) {
                HttpProxyConfig httpProxyConfig = this.getHttpProxyConfig();
                HttpHost proxy = new HttpHost(httpProxyConfig.getHost(), httpProxyConfig.getPort());
                this.httpClientBuilder.setProxy(proxy);
                // 设置代理认证账号密码
                if (!StringUtils.isAnyBlank(httpProxyConfig.getName(), httpProxyConfig.getPassword())) {
                    // 设置代理认证账号密码
                    BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
                    credentialsProvider.setCredentials(
                            new AuthScope(proxy),
                            new UsernamePasswordCredentials(httpProxyConfig.getName(), httpProxyConfig.getPassword().toCharArray()));
                    this.httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
                }
            }
        }
        // 创建HttpClient对象
        this.httpClient = this.httpClientBuilder.build();
        log.info("初始化Apache HttpClient5 完成");
    }

    @Override
    protected String sendGetRequest(String url) throws IOException {
        HttpGet httpGet = new HttpGet(url);
        Map<String, String> headerMap = buildHeaderMap();
        headerMap.forEach(httpGet::addHeader);
        return httpClient.execute(httpGet, responseHandler);
    }

    @Override
    protected String sendPostJsonRequest(String url, String jsonBody) throws IOException {
        HttpPost httpPost = new HttpPost(url);
        Map<String, String> headerMap = buildHeaderMap();
        headerMap.forEach(httpPost::addHeader);
        StringEntity entity = new StringEntity(jsonBody, StandardCharsets.UTF_8);
        httpPost.setEntity(entity);
        return httpClient.execute(httpPost, responseHandler);
    }

    @Override
    protected String sendPostFormData(String url, Map<String, Object> formMap, List<HttpFilePart> fileParts) throws IOException {
        HttpPost httpPost = new HttpPost(url);
        Map<String, String> headerMap = buildHeaderMap();
        headerMap.forEach(httpPost::addHeader);
        if (fileParts != null && !fileParts.isEmpty()) {
            MultipartEntityBuilder builder = MultipartEntityBuilder.create();
            for (Map.Entry<String, Object> entry : formMap.entrySet()) {
                if (entry.getValue() != null) {
                    builder.addTextBody(entry.getKey(), String.valueOf(entry.getValue()), ContentType.TEXT_PLAIN);
                }
            }
            for (HttpFilePart filePart : fileParts) {
                builder.addBinaryBody(filePart.getPartName(), filePart.getFileBytes(), ContentType.DEFAULT_BINARY, filePart.getFileName());
            }
            httpPost.setEntity(builder.build());
        } else {
            List<NameValuePair> form = new ArrayList<>();
            for (Map.Entry<String, Object> entry : formMap.entrySet()) {
                if (entry.getValue() != null) {
                    form.add(new BasicNameValuePair(entry.getKey(), String.valueOf(entry.getValue())));
                }
            }
            httpPost.setEntity(new UrlEncodedFormEntity(form, StandardCharsets.UTF_8));
        }
        return httpClient.execute(httpPost, responseHandler);
    }

    @Override
    protected String sendDeleteRequest(String url) throws IOException {
        HttpDelete httpDelete = new HttpDelete(url);
        Map<String, String> headerMap = buildHeaderMap();
        headerMap.forEach(httpDelete::addHeader);
        return httpClient.execute(httpDelete, responseHandler);
    }
}
